package com.yqb.study.common.lock;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

// 使用redis做分布式锁
@Component
@Slf4j
public class RedisLock {
	@Autowired
	private StringRedisTemplate redisTemplate;

	/**
	 * 加锁
	 * 
	 * @param key
	 * @param value
	 *            当前时间 + 超时时间
	 * @return
	 */
	public boolean lock(String key, String value) {
		if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
			// 只有该key不存在才进入
			return true;
		}

		// 可能加锁后,业务处理异常导致死锁,此处代码检查锁是否超时
		String currentValue = redisTemplate.opsForValue().get(key);
		if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
			// 锁已经超时,重新设置value,redis是单进程服务,只有一个线程可以拿到那个超时的oldValue
			String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
			if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
				// 通过此判断,可以防止两个线程同时设置新的value,加锁成功
				return true;
			}
		}

		return false;
	}

	/**
	 * 解锁
	 * 
	 * @param key
	 * @param value
	 */
	public void unlock(String key, String value) {
		try {
			String currentValue = redisTemplate.opsForValue().get(key);
			if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
				redisTemplate.opsForValue().getOperations().delete(key);
			}
		} catch (Exception e) {
			log.error("【redis分布式锁】解锁出现异常, {}", e);
		}
	}
}
